#ifndef _PACKETBUFFER_H_
#define _PACKETBUFFER_H_

#include <unistd.h>

#include "dispatcher.h"

class Session;

class Packet {
	union {
		uint8_t *_ipData;
		const uint8_t *_ipDataRO;
	};
	union {
		uint8_t *_tcpData;
		const uint8_t *_tcpDataRO;
	};

	struct Extra {
		Session *ses;
		bool closing;
		size_t ipLen;
		size_t tcpLen;

		Extra(Session *ses, bool closing, size_t ipLen, size_t tcpLen) :
			ses(ses), closing(closing), ipLen(ipLen),
			tcpLen(tcpLen) {}
		Extra() {}
	} _extra;

	friend class PacketBuffer;

public:
	Packet(Session *ses, uint8_t *ipData, size_t ipLen,
				uint8_t *tcpData, size_t tcpLen, bool closing) :
		_ipData(ipData), _tcpData(tcpData),
		_extra(ses, closing, ipLen, tcpLen) {}
	Packet(Session *ses, const uint8_t *ipData, size_t ipLen,
			const uint8_t *tcpData, size_t tcpLen, bool closing) :
		_ipDataRO(ipData), _tcpDataRO(tcpData),
		_extra(ses, closing, ipLen, tcpLen) {}
	Packet() {}

	inline const uint8_t *getIpData() const { return _ipData; }
	inline const uint8_t *getTcpData() const { return _tcpData; }
	inline size_t getIpLength() const { return _extra.ipLen; }
	inline size_t getTcpLength() const { return _extra.tcpLen; }
	inline Session *getSession() { return _extra.ses; }
	inline bool isClosing() const { return _extra.closing; }
};

class PacketBuffer {
	uint8_t *_data;
	size_t _size;

	size_t _head;
	size_t _tail;

	size_t _space;

	size_t _packets;
	pthread_cond_t _packetsAvailable;
	pthread_mutex_t _packetsAvailableMutex;

	pthread_mutex_t _mutex;

	void lock(void);
	void unlock(void);

	void popBytes(size_t pos);
	size_t getBytes(size_t pos, uint8_t *data, size_t len);
	size_t getData(size_t *avail);

	size_t putBytes(size_t pos, const uint8_t *data, size_t len);
	ssize_t getSpace(size_t length);

public:
	PacketBuffer(size_t size);
	~PacketBuffer();

	int push(const Packet &pkt);
	int pop(Packet &pkt);
};

#endif
